package com.bruce.cloud.feign.client;

import feign.MethodMetadata;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.cloud.openfeign.annotation.RequestParamParameterProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static feign.Util.checkState;

/**
 * Created by bruce in 2025/6/17 02:14
 */
@Configuration
public class AutoConfig {

   // @Bean
    public AnnotatedParameterProcessor requestParamFormParameterProcessor() {
        return new RequestParamFormParameterProcessor();
    }


    public static class RequestParamFormParameterProcessor extends RequestParamParameterProcessor {

        @Override
        public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
            int parameterIndex = context.getParameterIndex();
            Class<?> parameterType = method.getParameterTypes()[parameterIndex];
            MethodMetadata data = context.getMethodMetadata();

            if (Map.class.isAssignableFrom(parameterType)) {
                if (isPostForm(method)) {
                    checkState(data.queryMapIndex() == null, "Query map can only be present once.");
                    data.bodyIndex(parameterIndex);
                    data.bodyType(parameterType);
                    return true;
                }
            }

            return super.processArgument(context, annotation, method);
        }


        private boolean isPostForm(Method method) {
            Set<RequestMapping> requestMappings = AnnotatedElementUtils.findAllMergedAnnotations(method, RequestMapping.class);
            for (RequestMapping requestMapping : requestMappings) {
                if (isPostFormMapping(requestMapping)) {
                    return true;
                }
            }
            return false;
        }

        private boolean isPostFormMapping(RequestMapping requestMapping) {
            if (requestMapping.method().length == 0) {
                return false;
            }
            boolean isPost = Arrays.asList(requestMapping.method()).contains(RequestMethod.POST);
            if (isPost) {
                boolean isForm = Arrays.asList(requestMapping.consumes()).contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE);
                if (isForm) {
                    return true;
                }
                Map<String, String> headers = Stream.of(requestMapping.headers())
                        .map(pair -> pair.split("="))
                        .filter(arr -> arr.length == 2)
                        .collect(Collectors.toMap(arr -> arr[0].trim().toLowerCase(), arr -> arr[1].trim()));
                return headers.get("content-type").equals(MediaType.APPLICATION_FORM_URLENCODED_VALUE);
            }
            return false;
        }

    }


}
